﻿#light

namespace CodeBlock.AMD64

module Optimize =
    let DeclaredSymbols code =
        let rec declaredSymbols code result =
            match code with
            | [] -> result
            | op :: rest when op.Operation = Operation.Label ->
                let declared =
                    op.Operands
                    |> List.map (fun operand -> match operand with Immediate(imm) -> imm.Base)
                    |> Set.of_list
                declaredSymbols rest (declared + result)
            | op :: rest ->
                declaredSymbols rest result
        
        declaredSymbols code Set.empty
        
    let AllSymbols instruction =
        let rec allSymbols operands result =
            match operands with
            | [] -> result
            | Memory(mem) :: rest when mem.Displacement.Base <> null ->
                allSymbols rest (Set.add mem.Displacement.Base result)
            | Immediate(imm) :: rest when imm.Base <> null ->
                allSymbols rest (Set.add imm.Base result)
            | op :: rest ->
                allSymbols rest result
        
        if instruction.Operation = Operation.Comment then Set.empty
        else allSymbols instruction.Operands Set.empty
    
    let UsedSymbols code =
        let rec usedSymbols code result =
            match code with
            | [] -> result
            | op :: rest when op.Operation <> Operation.Label ->
                let opSymbols = AllSymbols op
                let result = opSymbols + result
                usedSymbols rest result
            | lbl :: rest when lbl.Operation = Operation.Label ->
                usedSymbols rest result
        
        usedSymbols code Set.empty
    
    let RemoveUnusedSymbols referenced code =
        let referenced = referenced + UsedSymbols code
        let referenced label = Set.exists (fun lbl -> lbl = label) referenced
        let unusedLabel op =
            if op.Operation = Operation.Label then
                match op.Operands with
                | Immediate(label) :: [] -> not(referenced label.Base)
                | _ -> failwith "can only be Immediate here"
            else
                false
        
        let filter op = 
            not(unusedLabel op)
        
        List.filter filter code
